package speed2010.andreza.vspeed.gui;

import java.awt.BorderLayout;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JSpinner;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import prefuse.Visualization;
import prefuse.action.Action;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.data.Graph;
import speed2010.andreza.vspeed.integration.CommunicationManager;
import speed2010.andreza.vspeed.view.ExportableGraphic;
import speed2010.andreza.vspeed.view.GraphDisplay;
import speed2010.andreza.vspeed.view.OWLGraphConverter;
import speed2010.util.SpeedLocalOntology;

public class PanelDisplayGraph extends javax.swing.JPanel implements ChangeListener, ExportableGraphic {
	private static final long serialVersionUID = 1306679012025292520L;

	private final static boolean GRAPH_DISTANCE_FILTER = true;

	/**
	 * The Prefuse Display which takes care of displaying the Prefuse graph.
	 */
	private GraphDisplay ontologyDisplay;
	private Graph ontologyGraph;

	/**
	 * The spinner, used for adjusting the distance of the GraphDistanceFilter.
	 */
	private JSpinner spinner;

	/**
	 * Creates a new instance of GraphPanel
	 * 
	 * @param panelVisualQuery
	 * 
	 * @param p_display
	 *            The Prefuse Display which has to be placed in this panel.
	 * @param p_legend
	 *            Indicates whether or not a legend should be added to the panel.
	 * @param p_hopsControl
	 *            Indicates whether or not a widget for controlling the hops in the graph should be added to the panel.
	 */
	public PanelDisplayGraph(final Object targetObject) {
		super(new BorderLayout());
		loadOntology(targetObject);
		initGUI();
	}

	private void loadOntology(final Object targetObject) {
		final SpeedLocalOntology OWL_FILE = CommunicationManager.getInstance().getSourceOntology();
		// Criando o grafo.
		final String ontologyLocation = OWL_FILE.ontology.getLocation();
		final OWLGraphConverter graphConverter = new OWLGraphConverter(ontologyLocation, true);
		this.ontologyGraph = graphConverter.getGraph();

		// Criando o grafo display.
		this.ontologyDisplay = new GraphDisplay(this.ontologyGraph, GRAPH_DISTANCE_FILTER, targetObject);
	}

	private void initGUI() {
		try {
			setBackground(this.ontologyDisplay.getBackground());
			setForeground(this.ontologyDisplay.getForeground());

			add(this.ontologyDisplay, BorderLayout.CENTER);
			add(createBox(), BorderLayout.SOUTH);
			setOpaque(true);
		} catch (final Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create the title and search panel widgets which are displayed at the bottom of the graph panel.
	 * 
	 * @return The title and searchpanel widgets in a Box.
	 */
	private Box createBox() {
		// Create a box to display the node title label and the search box.
		final Box box = new Box(BoxLayout.X_AXIS);
		box.add(Box.createHorizontalStrut(10));
		box.add(this.ontologyDisplay.getTitleLabel());
		box.add(Box.createHorizontalGlue());
		box.add(this.ontologyDisplay.getSearchPanel());
		box.add(Box.createHorizontalStrut(3));
		box.setBackground(this.ontologyDisplay.getBackground());
		box.setForeground(this.ontologyDisplay.getForeground());

		return box;
	}

	/**
	 * Export the display of the graph, to the given file type.
	 * 
	 * @param p_file
	 *            The file which is going to contain the export, valid file types are: "png" and "jpg".
	 * @param p_fileType
	 *            The file type of the image to be created.
	 */
	@Override
	public void export(final File p_file, final String p_fileType) {
		// Determine if there is a layout action running. If this is the case,
		// it
		// should be paused.
		final Visualization vis = this.ontologyDisplay.getVisualization();
		final Action action = vis.getAction("layout");
		boolean pause = false;
		if (action != null) {
			pause = action.isRunning();
		}
		if (pause) {
			vis.cancel("layout");
		}

		try {
			// Export the display to an image.
			final OutputStream out = new BufferedOutputStream(new FileOutputStream(p_file));
			this.ontologyDisplay.saveImage(out, p_fileType, 1.0);
			out.close();
		} catch (final IOException e) {
			e.printStackTrace();
		}

		// Run the layout action.
		if (pause) {
			vis.run("layout");
		}
	}

	/**
	 * This method is implemented because this class implements the ChangeListener interface. It takes care of the event
	 * that user changes the distance, used by the GraphDistanceFilter.
	 * 
	 * @param e
	 *            The ChangeEvent.
	 */
	@Override
	public void stateChanged(final ChangeEvent e) {
		final int value = ((Integer) this.spinner.getValue()).intValue();

		// Get the GraphDistanceFilter.
		final GraphDistanceFilter filter = this.ontologyDisplay.getDistanceFilter();
		filter.setDistance(value);
		filter.run();

		// Draw the nodes that are made visible by the adjustment of the hops
		// parameter.
		this.ontologyDisplay.getVisualization().run("draw");
	}

	/**
	 * @return Returns the ontologyDisplay.
	 */
	public GraphDisplay getOntologyDisplay() {
		return this.ontologyDisplay;
	}

	/**
	 * @return Returns the spinner.
	 */
	public JSpinner getSpinner() {
		return this.spinner;
	}

	/**
	 * Changes the value of spinner as the parameter.
	 * 
	 * @param spinner
	 */
	public void setSpinner(final JSpinner spinner) {
		this.spinner = spinner;
	}
}
